//
//  VPaiMediaWebSocket.swift
//  VPai
//
//  Created by devel on 16/9/20.
//  Copyright © 2016年 viatech. All rights reserved.
//

import Foundation
import SwiftWebSocket
import SwiftyJSON

class VPaiMediaWebSocket : NSObject {
    
    static let MR_NO_ERROR = 0;
    static let MR_STORAGE_ERROR = -1;
    static let MR_ALREADY_START = -2;
    static let MR_NOT_START = -3;
    static let MR_VIDEO_SOURCE_ERROR = -10;
    static let MR_VIDEO_ENCODER_ERROR = -11;
    static let MR_AUDIO_SOURCE_ERROR = -12;
    static let MR_AUDIO_ENCODER_ERROR = -13;
    static let MR_FFMPEG_ERROR = -20;
    static let MR_UNKOWN_ERROR = -100;
    
    //单例模式
    class var shareInstance : VPaiMediaWebSocket{
        struct Static {
            static var instance: VPaiMediaWebSocket?
            static var token: dispatch_once_t = 0
        }
        
        dispatch_once(&Static.token){
            Static.instance = VPaiMediaWebSocket()
        }
        
        return Static.instance!
    }
    
    func showErrorText(errcode: Int) {
        if errcode == VPaiMediaWebSocket.MR_NO_ERROR {
            return
        }
        
        var text = "unknown error".i18n
        switch errcode {
        case VPaiMediaWebSocket.MR_STORAGE_ERROR:
            text = "storage error".i18n
        case VPaiMediaWebSocket.MR_ALREADY_START:
            text = "already start".i18n
        case VPaiMediaWebSocket.MR_NOT_START:
            text = "not start error".i18n
        case VPaiMediaWebSocket.MR_VIDEO_SOURCE_ERROR:
            text = "video soruce error".i18n
        case VPaiMediaWebSocket.MR_VIDEO_ENCODER_ERROR:
            text = "video encode error".i18n
        case VPaiMediaWebSocket.MR_AUDIO_SOURCE_ERROR:
            text = "audio source error".i18n
        case VPaiMediaWebSocket.MR_AUDIO_ENCODER_ERROR:
            text = "audio encoder error".i18n
        case VPaiMediaWebSocket.MR_FFMPEG_ERROR:
            text = "ffmpeg error".i18n
        case VPaiMediaWebSocket.MR_UNKOWN_ERROR:
            text = "unknown error".i18n
        default:
            text = "unknown error".i18n
        }
       // Toast.showToast(text)
        
    }
    
    func openWebSocket(url: String){
       // Log.info(VPaiMediaWebSocket.TAG, msg: "openWebSocket:url = \(url)")
        websocketUrl = url
        if(_ws != nil){
            closeWebSocket()
        }
        
        let ws = WebSocket(url)
        ws.event.open = onWSOpen
        ws.event.end = onWSEnd
        ws.event.message = onWSMessage
        _ws = ws;
    }
    
    func closeWebSocket(){
        _ws?.close()
        _ws = nil
        connected = false
        //clear callback map also
        for t in _replyTimer {
            t.1.invalidate()
        }
        for k in _replyMap {
            let args = CallbackArgs()
            k.1(args: args)
        }
        _replyMap.removeAll()
        _replyTimer.removeAll()
    }
    
    func toggleRec(){
        let json = SwiftyJSON.JSON([
            "cmd" : CMD_TOGGLE_RECORD
            ])
        sendStringData(json)
    }
    
    func takePic(cb: CallbackFunc){
        let json = SwiftyJSON.JSON([
            "cmd" : CMD_TAKE_PIC
            ])
        sendStringData(json, cb: cb)
    }
    
//    // 获取文件列表
//    func getFileList(cb: CallbackFunc) {
//        let json = SwiftyJSON.JSON([
//            "action" : CMD_FILE_LIST
//            ])
//        sendStringData(json, cb: cb)
//    }
    
    func startPrev(cb: CallbackFunc){
        let json = SwiftyJSON.JSON([
            "cmd" : CMD_START_PREV
            ])
        sendStringData(json, cb: cb)
    }
    
    func stopPrev(cb: CallbackFunc){
        let json = SwiftyJSON.JSON([
            "cmd" : CMD_STOP_PREV
            ])
        sendStringData(json, cb: cb)
    }
    
    func setResolution(widht: Int, height: Int) {
        let json = SwiftyJSON.JSON([
            "cmd" : CMD_SET_RESOLUTION,
            "width" : widht,
            "height" : height
            ])
        sendStringData(json)
    }
    
    func setAudio(on: Bool) {
        let json = SwiftyJSON.JSON([
            "cmd" : CMD_AUDIO_ON,
            "audio" : (on ? 1 : 0)
            ])
        sendStringData(json)
    }
    
    //and set onXXX function. You should save it and call unregisterEvent later.
    func registerEvent() -> VPaiMediaWebSocketEvents{
        let e = VPaiMediaWebSocketEvents()
        e.id = _nextEventId
        _nextEventId += 1
        _listenEvents.append(e)
        return e
    }
    
    //unregister it. e should be the return value of registerEvent()
    func unregisterEvent(e : VPaiMediaWebSocketEvents){
        var idx = 0
        for d in _listenEvents {
            if d.id == e.id {
                _listenEvents.removeAtIndex(idx)
                return
            }
            idx += 1
        }
    }
    
    func sendStringData(json: SwiftyJSON.JSON, cb:CallbackFunc?=nil){
        if let ws = _ws where connected {
            _cookie += 1
            var newJson = json
            newJson["cookie"] = SwiftyJSON.JSON(_cookie)
            
            if cb != nil {
                _replyTimer[_cookie] = NSTimer.scheduledTimerWithTimeInterval(timeout, target: self,
                        selector: #selector(VPaiMediaWebSocket.sentTimeout(_:)),userInfo: _cookie, repeats: false)
                _replyMap[_cookie] = cb
               // Log.info(VPaiMediaWebSocket.TAG, msg: "sendStringData:\(cb) :\(_cookie) \(_replyMap[_cookie])")
            }
           // Log.info(VPaiMediaWebSocket.TAG, msg: "sendStringData:\(newJson.rawString()!)")
            ws.send(text: newJson.rawString()!)
        }else {
           // Log.info(VPaiMediaWebSocket.TAG, msg: "sendStringData: no connection")
            dispatch_async(dispatch_get_main_queue(), {
                let args = CallbackArgs()
                cb?(args: args)
            })
        }
    }
    
    func sentTimeout(t: NSTimer) {
        if let cookie = t.userInfo as? Int {
            _replyTimer[cookie]?.invalidate()
            _replyTimer[cookie] = nil
            if let cb = _replyMap[cookie] {
                _replyMap[cookie] = nil
                let args = CallbackArgs()
                dispatch_async(dispatch_get_main_queue(), {
                    cb(args: args)
                })
            }
        }
    }
    
    private static let TAG = "VPaiMediaWebSocket"
    private let CMD_START_REC = "startRec"
    private let CMD_STOP_REC = "stopRec"
    private let CMD_TAKING_PIC = "takingPic" // 正在拍照
    private let CMD_TAKE_PIC = "takePic"
//    private let CMD_FILE_LIST = "filelist" // 获取列表
    private let CMD_START_PREV = "startPrv"
    private let CMD_STOP_PREV = "stopPrv"
    private let CMD_TOGGLE_RECORD = "toggleRec";
    private let CMD_SET_RESOLUTION = "setResolution";
    private let CMD_AUDIO_ON = "audio";
    private var _ws : WebSocket?
    private var connected = false
    private var _nextEventId:Int = 1
    private var _listenEvents : [VPaiMediaWebSocketEvents] = []
    private var websocketUrl = ""
    //Key is cookie(Int type), vaule is a callback clousure
    private var _replyMap: Dictionary<Int, CallbackFunc> = [:]
    private var _replyTimer: Dictionary<Int, NSTimer> = [:]
    private var timeout : NSTimeInterval = 10
    private var _cookie = 1
    
    private func onWSOpen(){
      //  Log.info(VPaiMediaWebSocket.TAG, msg: "onWSOpen()")
        connected = true
        let width = VPaiDeviceSetting.shareInstance.videoResolutionWidth
        let height = VPaiDeviceSetting.shareInstance.videoResolutionHeight
        setResolution(width, height: height)
        setAudio(VPaiDeviceSetting.shareInstance.previewSound)
        dispatch_async(dispatch_get_main_queue(), {
            for e in self._listenEvents {
                e.onWebSocketOpen()
            }
        })
    }
    
    private func onWSEnd(code : Int, reason : String, wasClean : Bool, error : ErrorType?){
       // Log.info(VPaiMediaWebSocket.TAG, msg: "onWSEnd: code=\(code), reason=\(reason)")
        closeWebSocket()
        dispatch_async(dispatch_get_main_queue(), {
            for e in self._listenEvents {
                e.onWebSocketClose()
            }
        })
    }
    
    private func onWSMessage(data : Any){
        if let text = data as? String {
       //     Log.info(VPaiMediaWebSocket.TAG, msg: "text = \(text)")
            let json = SwiftyJSON.JSON.parse(text)
            let cookie = json["cookie"].int ?? -1
            let cmd = json["cmd"].string ?? "unknow"
            
            if let cb = _replyMap[cookie] {
                // FIXME: 拍照命令特殊处理,拍照会返回takingPic和takePic两个响应
                if cmd == CMD_TAKING_PIC {
                    return
                }
                _replyTimer[cookie]?.invalidate()
                _replyTimer[cookie] = nil
                _replyMap[cookie] = nil
                
                let args = CallbackArgs()
                args.ret = json["ret"].int ?? 0
                args.flag = json["flag"].int ?? 0
                args.json = json
                dispatch_async(dispatch_get_main_queue(), {
                    cb(args: args)
                })
                return
            }
    
            switch cmd {
            case CMD_START_REC:
                handleStartRec(json)
                break
            case CMD_STOP_REC:
                handleStopRec(json)
                break
            default: break
       //         Log.error(VPaiMediaWebSocket.TAG, msg: "unknow cmd:\(cmd)")
            }
        }else if var uint8data = data as? [UInt8]{
            var idx = 0
            for uint in uint8data{
                if(uint == 0){
                    break
                }
                idx += 1
            }
            let str = NSString(bytes:uint8data,length: idx, encoding: NSUTF8StringEncoding)
            if str != nil {
                let json = SwiftyJSON.JSON.parse(str as! String)
                uint8data.removeRange(Range(0...idx))
                let args = CallbackArgs()
                args.ret = json["ret"].int ?? 0
                args.json = json
                args.binary = uint8data
                handleStartPrev(args)
                return

            }
            //Log.info(VPaiMediaWebSocket.TAG, msg: "onWSMessage:\(idx) :\(str)")
            
        }
    }
    
    private func handleStartRec(json: SwiftyJSON.JSON){
        let info = StartRecInfo.parseFromJson(json)
        dispatch_async(dispatch_get_main_queue(), {
            for e in self._listenEvents {
                e.onStartRec(info: info)
            }
        })
    }
    
    private func handleStopRec(json: SwiftyJSON.JSON){
        let info = StopRecInfo.parseFromJson(json)
        dispatch_async(dispatch_get_main_queue(), {
            for e in self._listenEvents {
                e.onStopRec(info: info)
            }
        })
    }
    private func handleStartPrev(args: CallbackArgs){
        for e in self._listenEvents {
            e.onStartPrev(args: args)
        }
    }
    private func handleStopPrev(json: SwiftyJSON.JSON){
        let info = StopPrevInfo.parseFromJson(json)
        dispatch_async(dispatch_get_main_queue(), {
            for e in self._listenEvents {
                e.onStopPrev(info: info)
            }
        })
    }
}


class VPaiMediaWebSocketEvents: NSObject{
    
    var onWebSocketOpen : () -> () = {}
    var onWebSocketClose : () -> () = {}
    var onStartRec : (info: StartRecInfo) -> () = {info in }
    var onStopRec : (info: StopRecInfo) -> () = {info in }
    var onStartPrev : (args: CallbackArgs) -> () = {args in }
    var onStopPrev : (info: StopPrevInfo) -> () = {info in }

    //used to compare object when call unregisterEvent
    private var id : Int = 0
}

class StartRecInfo {
    var ret = 0
    var cookie = 0
    var recordtime = 0
    
    static func parseFromJson(json: SwiftyJSON.JSON) -> StartRecInfo{
        let info = StartRecInfo()
        info.ret = json["ret"].int ?? 0
        info.cookie = json["cookie"].int ?? 0
        info.recordtime = json["recordtime"].int ?? 0
        return info
    }
}

class StopRecInfo {
    var ret = 0
    var cookie = 0
    var filename = ""
    
    static func parseFromJson(json: SwiftyJSON.JSON) -> StopRecInfo{
        let info = StopRecInfo()
        info.ret = json["ret"].int ?? 0
        info.cookie = json["cookie"].int ?? 0
        info.filename = json["filename"].string ?? ""
        return info
    }
}

class TakePicInfo {
    var ret = 0
    var cookie = 0
    var filename = ""
    
    static func parseFromJson(json: SwiftyJSON.JSON) -> TakePicInfo{
        let info = TakePicInfo()
        info.ret = json["ret"].int ?? 0
        info.cookie = json["cookie"].int ?? 0
        info.filename = json["filename"].string ?? ""
        return info
    }
}

class StartPrevInfo {
    var ret = 0
    var cookie = 0
    
    static func parseFromJson(json: SwiftyJSON.JSON) -> StartPrevInfo{
        let info = StartPrevInfo()
        info.ret = json["ret"].int ?? 0
        info.cookie = json["cookie"].int ?? 0
        return info
    }
}

class StopPrevInfo {
    var ret = 0
    var cookie = 0
    
    static func parseFromJson(json: SwiftyJSON.JSON) -> StopPrevInfo{
        let info = StopPrevInfo()
        info.ret = json["ret"].int ?? 0
        info.cookie = json["cookie"].int ?? 0
        return info
    }
}
